/**
 * @brief 
 * @author zhangpeihao
 */

#pragma once

#include <zsystem/core/common/event_types.h>
#include <zsystem/core/common/logdump.h>
#include <zsystem/core/networking/connection.h>

namespace zsystem{

  class tcpServerST;
  typedef zsystem::stdMap<Z_I32, zsystem::noLock> tcpServerSTConnectionMap;

  // Signal thread model(No lock)
  class tcpServerSTConnection : public tcpConnection {
  public:
    virtual ~tcpServerSTConnection();
    void close();
  protected:
    tcpServerSTConnection(const Z_U32 id, 
                          zsystem::tcpServerST* server, 
                          zsystem::tcpSocket* socket);
    void disconnect();
    virtual void onConnected(tcpConnection* connection){}
    virtual void onDisconnected(tcpConnection* connection){}
    zsystem::tcpServerST* _server;
  friend class zsystem::tcpServerST;
  };

  typedef std::queue<tcpServerSTConnection*> tcpServerSTConnectionQueue;
  class tcpServerST : public listenerContainer {
  public:
    tcpServerST(zsystem::Z_EV_BASE* base, 
                zsystem::msgReceivedListener defaultListener, 
                const char* bindAddress, 
                const Z_I32 sendBufferSize, 
                const Z_I32 recieveBufferSize, 
                const Z_I32 keepAliveInterval, 
                const Z_I32 backlog);
    virtual ~tcpServerST();
    const Z_BOOL start();
    void stop();
    virtual const Z_BOOL close(const Z_I32 connectionId);
    const Z_I32 getConnectionNumber() const{
      return _connectionMapNumber;
    }
    const Z_I32 getReleaseConnectionNumber() const{
      return _releaseConnectionNumber;
    }
    zsystem::Z_EV_BASE* getBase(){return _evbase;}
    static const Z_BOOL md5Check(const Z_U8* const data, const Z_U32 dataLength, const std::string& sign);
    static const Z_BOOL md5Check(const Z_U8* const data, const Z_U32 dataLength, const Z_U8 sign[16]);
    zsystem::tcpConnection* findConnection(const Z_I32 connectionId) {
      Z_PTR ret(Z_NULL);
      if( _connectionMap->lookup(connectionId, ret) ){
        return static_cast<zsystem::tcpConnection*>(ret);
      }
      return Z_NULL;
    }

  private:
    static void onAccept(zsystem::Z_EV_SRV* srv, evutil_socket_t nfd, struct sockaddr *peer_sa, int peer_socklen, Z_PTR arg);
    static void onUpdateTimestamp(Z_SOCKET_HANDLE fd, Z_I16 ev, Z_PTR arg);
    void releaseConnections();
  protected:
    char* _bindAddress;
    zsystem::tcpSocket* _listenSocket;
    zsystem::tcpServerSTConnectionMap* _connectionMap;
    tcpServerSTConnectionQueue* _releaseConnectionQueue;
    zsystem::tcpServerSTConnection* _sendConnection;
    Z_EV_BASE* _evbase;
    Z_EV_SRV* _evsrv;
    Z_EV* _timerEvent;
    Z_I32 _sendBufferSize;
    Z_I32 _recieveBufferSize;
    Z_I32 _keepAliveInterval;
    Z_I32 _backlog;
    Z_I32 _connectionMapNumber;
    Z_I32 _releaseConnectionNumber;
    Z_AU32 _connectionIdAlloc;
    struct timeval _tv;
  friend class tcpServerConnection;
  };
#if 0
  class tcpServerSTConnectionManager : public zsystem::connectionManager
  {
  public:
    tcpServerSTConnectionManager(const Z_I32 serverNumber)
    : _servers (Z_NULL)
    , _serverNumber(serverNumber){
      _servers = (tcpServerST**)Z_MALLOC(sizeof(Z_PTR) * _serverNumber);
      for ( int i = 0; i < _serverNumber; i++){
        _servers[i] = Z_NULL;
      }
    }
    ~tcpServerSTConnectionManager(){
      if ( _servers != Z_NULL ){
        Z_FREE(_servers);
      }
    }
    zsystem::tcpConnection* findConnection(const Z_I32 connectionId) {
      zsystem::tcpConnection* connection(Z_NULL);
      for ( int i = 0; i < _serverNumber && _servers[i] != Z_NULL; i++) {
        connection = _servers[i]->findConnection(connectionId);
        if ( connection != Z_NULL )
          return connection;
      }
      return connection;
    }
    zsystem::tcpConnection* newConnection()
    {
      return Z_NULL;
    }
    void addServer(tcpServerST* server){
      for ( int i = 0; i < _serverNumber; i++){
        if ( _servers[i] == Z_NULL ) {
          _servers[i] = server;
          return;
        }
      }
    }
    const Z_U32 newConnectionId() {
      return _connectionIdAlloc.inc();
    }
  protected:
    tcpServerST** _servers;
    Z_I32 _serverNumber;
    Z_AU32 _connectionIdAlloc;
  };
#endif
}

